package com.quanyue.haircut.order.service.impl;

import com.quanyue.auto.util.AssertUtils;
import com.quanyue.auto.util.KeyGenerator;
import com.quanyue.haircut.domain.constant.CouponType;
import com.quanyue.haircut.domain.constant.NoPrefix;
import com.quanyue.haircut.domain.constant.OrderStatus;
import com.quanyue.haircut.domain.constant.PayResult;
import com.quanyue.haircut.domain.entity.*;
import com.quanyue.haircut.order.dto.ReserveOrderCreate;
import com.quanyue.haircut.order.dto.ReserveOrderDetail;
import com.quanyue.haircut.order.dto.UserByConsume;
import com.quanyue.haircut.order.dto.UserByConsumeReq;
import com.quanyue.haircut.order.mapper.ReserveInfoMapper;
import com.quanyue.haircut.order.mapper.ReserveOrderMapper;
import com.quanyue.haircut.order.service.ReserveOrderService;
import com.quanyue.haircut.shop.mapper.CardScopeMapper;
import com.quanyue.haircut.shop.mapper.CouponMapper;
import com.quanyue.haircut.shop.mapper.CouponUserMapper;
import com.quanyue.haircut.shop.mapper.ShopMapper;
import com.quanyue.haircut.shop.service.CardService;
import com.quanyue.haircut.shop.service.CouponUserService;
import com.quanyue.haircut.shop.service.IntegralService;
import com.vhuomai.mybatis.mapper.page.Page;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.util.List;

@Service
public class ReserveOrderServiceImpl implements ReserveOrderService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private ReserveOrderMapper reserveOrderMapper;
    @Autowired
    private ReserveInfoMapper reserveInfoMapper;
    @Autowired
    private IntegralService integralService;
    @Autowired
    private CouponUserMapper couponUserMapper;
    @Autowired
    private ShopMapper shopMapper;
    @Autowired
    private CouponMapper couponMapper;
    @Autowired
    private CouponUserService couponUserService;
    @Autowired
    private CardService cardService;
    @Autowired
    private CardScopeMapper cardScopeMapper;

    @Override
    @Transactional
    public ReserveOrder create(Integer merchantNo, User user, ReserveOrderCreate orderCreate) {
        BigDecimal originalPrice = orderCreate.getReserveOrder().getOriginalPrice();
        if (originalPrice == null || originalPrice.compareTo(BigDecimal.ZERO) == 0) {
            throw new AssertUtils.ValidateException("订单原价不能为0或null");
        }
        ReserveInfo reserveInfo = orderCreate.getReserveInfo();
        reserveInfo.setCreateTime(LocalDateTime.now());
        reserveInfo.setModifyTime(LocalDateTime.now());
        reserveInfo.setDelFlag(false);
        reserveInfoMapper.insert(reserveInfo);
        // 记录订单信息
        ReserveOrder reserveOrder = orderCreate.getReserveOrder();
        reserveOrder.setMerchantNo(merchantNo);
        reserveOrder.setOrderInfo(reserveInfo.getId());
        if (reserveOrder.getUserId() == null) {
            reserveOrder.setUserId(user.getId());
        }
        reserveOrder.setOrderNo(KeyGenerator.createOrderNo(NoPrefix.ORDER.value(), merchantNo, KeyGenerator.uuid()));
        reserveOrder.setPayPrice(reserveOrder.getOriginalPrice());
        reserveOrder.setDiscountPrice(BigDecimal.ZERO);
        reserveOrder.setCreateTime(LocalDateTime.now());
        reserveOrder.setModifyTime(LocalDateTime.now());
        reserveOrder.setOrderStatus(OrderStatus.SUBMIT);
        reserveOrder.setDelFlag(false);
        if (!orderCreate.getOffline()) {
            // 计算优惠价格
            if (orderCreate.getUserCoupon() != null) {
                BigDecimal payPrice = this.discount(orderCreate.getUserCoupon(), reserveOrder.getOriginalPrice());
                reserveOrder.setPayPrice(payPrice);
            }
            // 计算会员卡折扣
            BigDecimal pay = reserveOrder.getPayPrice().multiply(shopMapper.findOne(reserveOrder.getShopId()).getCardDiscount());
            Card card = cardService.findCardByUserAndShop(reserveOrder.getUserId(), reserveOrder.getShopId());
            CardScope scope = cardScopeMapper.findByMerchantNo(merchantNo);
            if (card != null && card.getBalance().compareTo(pay) >= 0) {
                reserveOrder = this.updateCardBalance(card, pay, reserveOrder);
            } else if (scope == null ? true : scope.getIsCommon()) {
                List <Card> cardList = cardService.findCardByUser(reserveOrder.getUserId());
                for (Card c : cardList) {
                    if (c.getBalance().compareTo(pay) >= 0) {
                        reserveOrder = this.updateCardBalance(c, pay, reserveOrder);
                        break;
                    }
                }
            }
        } else {
            reserveOrder.setOrderStatus(OrderStatus.DONE);
        }
        reserveOrder.setDiscountPrice(reserveOrder.getOriginalPrice().subtract(reserveOrder.getPayPrice()));
        reserveOrderMapper.insert(reserveOrder);
        return reserveOrder;
    }

    private ReserveOrder updateCardBalance(Card card, BigDecimal payPrice, ReserveOrder reserveOrder) {
        card.setBalance(card.getBalance().subtract(payPrice));
        card.setModifyTime(LocalDateTime.now());
        cardService.updateCard(card);
        reserveOrder.setPayPrice(payPrice);
        reserveOrder.setCardNo(card.getCardNo());
        reserveOrder.setOrderStatus(OrderStatus.DONE);
        return reserveOrder;
    }

    @Override
    public ReserveOrder findOrderByNo(String orderNo) {
        return reserveOrderMapper.findByOrderNo(orderNo);
    }

    @Override
    @Transactional
    public void afterPayComplete(String orderNo, String payId, PayResult result) {
        ReserveOrder order = this.findOrderByNo(orderNo);
        if (order.getOrderStatus().equals(OrderStatus.DONE)) return;
        if (result == PayResult.SUCCESS) {
            // 更新订单号
            order.setPayId(payId);
            order.setModifyTime(LocalDateTime.now());
            order.setOrderStatus(OrderStatus.DONE);
            reserveOrderMapper.updateSelective(order);
            // 更新会员积分
            UserIntegral userIntegral = integralService.findIntegral(order.getUserId(), order.getShopId());
            BigInteger integer = BigInteger.valueOf(order.getPayPrice().intValue());
            if (userIntegral != null) integer = userIntegral.getIntegral().add(integer);
            integralService.updateIntegral(order.getShopId(), order.getUserId(), integer);
        } else {
            logger.error("weixin pay fail: ", result.name());
        }
    }

    @Override
    public List <ReserveOrderDetail> findOrderDetailList(Integer userId, Integer shopId) {
        return reserveOrderMapper.findOrderList(userId, shopId);
    }

    @Override
    public BigDecimal discount(Integer couponId, BigDecimal originalPrice) {
        UserCoupon userCoupon = couponUserMapper.findOne(couponId);
        AssertUtils.notNull(userCoupon, "没有匹配到该优惠券");
        Coupon coupon = couponMapper.findOne(userCoupon.getCouponId());
        couponUserService.deleteCouponUser(userCoupon.getId());
        if (coupon.getCouponType().equals(CouponType.FULL_REDUCTION)) {
            return originalPrice.subtract(coupon.getSave());
        }
        return originalPrice.multiply(coupon.getDiscount());
    }

    @Override
    public Page <UserByConsume> findVipByConsume(UserByConsumeReq req) {
        req.getRequest().setAsc(false);
        req.getRequest().setOrderBy("consume_price");
        Page <UserByConsume> userByConsumePage = null;
        if (req.getConsumePrice().compareTo(BigDecimal.ZERO) == 0 && req.getConsumeCount() == 0) {
            req.getRequest().setOrderBy("c.balance");
            userByConsumePage = reserveOrderMapper.findVipByShop(req.getShopId(), req.getRequest());
            userByConsumePage.getContent().forEach(userByConsume -> {
                userByConsume.setConsumeCount(0);
                userByConsume.setConsumePrice(0);
            });
        } else {
            userByConsumePage = reserveOrderMapper.findVipByConsume(req.getShopId(), req.getConsumeCount(),
                    req.getConsumePrice(), req.getRequest());
        }
        return userByConsumePage;
    }
}
